home *** CD-ROM | disk | FTP | other *** search
/ Adobe Graphics & Publishing SDK 1996 December / Adobe Graphics & Publishing SDK 1996 December.iso / mac / Premiere 4.2 SDK r3 Mac / Examples / Projects / Generic EDL / Generic EDL.c < prev    next >
Text File  |  1996-01-25  |  21KB  |  637 lines

  1. //========================================================================================
  2. //
  3. // Generic EDL.c - A generic edit decision list export module.
  4. //
  5. // Written by Randy Ubillos and Bryan K. "Beaker" Ressler.
  6. //
  7. // Copyright ⌐ 1993-96, Adobe Systems Incorporated, all rights reserved worldwide.
  8. //
  9. // Version    1.00    10/20/93    Original version.
  10. // Version    1.01    9/12/95        Updated for 4.0.
  11. // Version  1.02    10/8/95     Updated for 4.2 and CW7.
  12. //
  13. // NOTE TO DEVELOPERS: This file contains a number of routines that are useful in the
  14. // parsing of Premiere's nested block EDL data structure. Not all the routines are
  15. // actually used by the Generic EDL -- they are provided in case you need them.
  16. //
  17. //========================================================================================
  18.  
  19. //========================================================================================
  20. // Includes - use precompiled headers if compiling with CodeWarrior.
  21. //========================================================================================
  22. #ifdef __MWERKS__
  23.     #ifdef powerc
  24.         #include "PremierePPC"
  25.     #else
  26.         #include "Premiere68k"
  27.     #endif
  28. #else
  29.     #include "Premiere.h"
  30. #endif
  31.  
  32. #include <stdarg.h>
  33.  
  34. //========================================================================================
  35. // Constants
  36. //========================================================================================
  37. enum {                    // Resource IDs
  38.     grStrings = 128,    // STR# - standard file strings
  39.     grKeyTypes = 129    // STR# - names of the key types
  40. };
  41.  
  42. enum {                    // Strings in our STR# grStrings
  43.     gsPrompt = 1,
  44.     gsSuffix = 2
  45. };
  46.  
  47. //========================================================================================
  48. // Static prototypes
  49. //========================================================================================
  50. static short WriteFile(short ref, Ptr data, long length);
  51. static short SendChar(short ref, char ch);
  52. static short WriteString(short ref, char *str);
  53. static short SendNum(short ref, long num);
  54. static short SendStr(short ref, char *fmt, ...);
  55. static short SendIndent(short ref, short amount);
  56. static short ParseBlock(BlockRec *theblock, short ref, short indent);
  57.  
  58. //========================================================================================
  59. // Export module entry point
  60. //========================================================================================
  61. pascal short main (short selector, ExportHandle theData)
  62. {
  63.     Str63                prompt, defaultName, suffix;
  64.     StandardFileReply    reply;
  65.     short                result = 0, err, ref;
  66.  
  67.     // Act according to the selector
  68.     switch (selector) {
  69.         case exExecute:
  70.             // Get the standard put file prompt and the EDL filename suffix from our
  71.             // string list. Append the suffix to the current project name.
  72.             GetIndString(prompt, grStrings, gsPrompt);
  73.             GetIndString(suffix, grStrings, gsSuffix);
  74.             BlockMove((*theData)->projectName, defaultName,
  75.                 (*theData)->projectName[0] + 1);
  76.             Append(defaultName, suffix);
  77.             
  78.             // Ask the user for an output filespec.
  79.             StandardPutFile(prompt, defaultName, &reply);
  80.             if (reply.sfGood) {
  81.                 // Delete any existing file with that name, then create and open a new
  82.                 // text file as specified by the user.
  83.                 FSpDelete(&reply.sfFile);
  84.                 // The following is ???? followed by TEXT
  85.                 err = FSpCreate(&reply.sfFile, 0x3f3f3f3f, 0x54455854, reply.sfScript);
  86.                 if (err == noErr)
  87.                     err = FSpOpenDF(&reply.sfFile, fsRdWrPerm, &ref);
  88.                 
  89.                 // Now call ParseBlock to parse the project and output an EDL to the
  90.                 // text file.
  91.                 if (err == noErr) {
  92.                     HLock((*theData)->dataHandle);
  93.                     ParseBlock((BlockRec *)*(*theData)->dataHandle, ref, 0);
  94.                     HUnlock((*theData)->dataHandle);
  95.                     FSClose(ref);
  96.                     MakeWindowForTextFile(&reply.sfFile);
  97.                 }
  98.             }
  99.             break;
  100.             
  101.         case exTrue30fps:
  102.             // Return true here to get time values in 1/30 instead of 1/29.97.
  103.             result = true;
  104.             break;
  105.     }
  106.     return(result);
  107. }
  108.  
  109. //========================================================================================
  110. // Write data to a file
  111. //========================================================================================
  112. static short WriteFile(short ref, Ptr data, long length)
  113. {
  114.     long    count;
  115.     
  116.     count = length;
  117.     return(FSWrite(ref, &count, data));
  118. }
  119.  
  120. //========================================================================================
  121. // Send a character to a file
  122. //========================================================================================
  123. static short SendChar(short ref, char ch)
  124. {
  125.     return(WriteFile(ref, &ch, 1));
  126. }
  127.  
  128. //========================================================================================
  129. // Send a string to a file
  130. //========================================================================================
  131. static short WriteString(short ref, char *str)
  132. {
  133.     return(WriteFile(ref, str + 1, str[0]));
  134. }
  135.  
  136. //========================================================================================
  137. // Send a number to a file
  138. //========================================================================================
  139. static short SendNum(short ref, long num)
  140. {
  141.     Str127    str;
  142.     
  143.     NumToString(num, str);
  144.     return(WriteString(ref, (char *)str));
  145. }
  146.  
  147. //========================================================================================
  148. // Send a formatted string to a file. The parameter fmt is a format string akin to that
  149. // of printf (see table below)
  150. //
  151. // Specifier    Parameter    Description
  152. // ------------ ----------- ------------------------------------------------------------
  153. //        %d        long        Substitute a long integer value output in decimal
  154. //        %s        char *        Substitute a string
  155. //        %t        OSType        Substitute an OSType value (output as a 4-character string)
  156. //        %b        char        Substitute a byte value output in binary
  157. //========================================================================================
  158. static short SendStr(short ref, char *fmt, ...)
  159. {
  160.     Str31    temp;
  161.     va_list    parm;
  162.     long    parms[kMaxExpandParms];
  163.     char    *where, *last, *start, oldChar;
  164.     short    param = 0, err = 0, i, num;
  165.     
  166.     // Unroll the parameters in advance (required for PowerPC)
  167.     va_start(parm, fmt);
  168.     for (i = 0; i < kMaxExpandParms; i++)
  169.         parms[i] = va_arg(parm, long);
  170.     va_end(parm);
  171.     
  172.     where = fmt + 1;
  173.     last = fmt + fmt[0];
  174.     while (err == noErr && (where <= last)) {
  175.         start = where;
  176.         if (where[0] == '%') {
  177.             switch (where[1]) {
  178.                 case 'd':                                            // %d means decimal
  179.                     err = SendNum(ref, parms[param++]);
  180.                     break;
  181.                 case 's':                                            // %s means string
  182.                     err = WriteString(ref, (char *)parms[param++]);
  183.                     break;
  184.                 case 't':                                            // %t means OSType
  185.                     temp[0] = 4;
  186.                     *((long*)(temp + 1)) = parms[param++];
  187.                     err = WriteString(ref, (char *)temp);
  188.                     break;
  189.                 case 'b':                                            // %b means binary
  190.                     temp[0] = 8;
  191.                     num = parms[param++];
  192.                     for (i = 0; i <= 7; i++)
  193.                         temp[1 + i] = num & (0x0080 >> i) ? '1' : '0';
  194.                     err = WriteString(ref, (char *)temp);
  195.                     break;
  196.             }
  197.             where += 2;
  198.         } else {                                                    // no substitution
  199.             while ((where <= last) && (where[0] != '%'))
  200.                 where++;
  201.             oldChar = start[-1];
  202.             start[-1] = where - start;
  203.             err = WriteString(ref, (char *)(start - 1));
  204.             start[-1] = oldChar;
  205.         }
  206.     }
  207.     return(err);
  208. }
  209.  
  210. //========================================================================================
  211. // Send out spaces to do an indent
  212. //========================================================================================
  213. static short SendIndent(short ref, short amount)
  214. {
  215.     short    i, err = 0;
  216.     
  217.     for (i = 0; i < amount && err == noErr; i++)
  218.         err = SendChar(ref, 9);
  219.     return(err);
  220. }
  221.  
  222. //========================================================================================
  223. // Take apart a block -- this routine is called recursively.
  224. //========================================================================================
  225. static short ParseBlock(BlockRec *theBlock, short ref, short indent)
  226. {
  227.     short        i, count, sValue, err = 0, mapping;
  228.     long        len, lValue;
  229.     Rec_BLOK    rBLOK;
  230.     Rec_TREC    rTREC;
  231.     Rec_CLIP    rCLIP;
  232.     Rec_RPNT    rRPNT;
  233.     Rec_MREC    rMREC;
  234.     Rec_VIDI    rVIDI;
  235.     Rec_FXOP    rFXOP;
  236.     Rec_TIMB    rTIMB;
  237.     FSSpec        rFILE;
  238.     RGBColor    theColor;
  239.     Rect        box;
  240.     Point        pt;
  241.     Str31        str;
  242.     Ptr            pblock, p;
  243.     
  244.     // Indent and open this block
  245.     err = SendIndent(ref, indent);
  246.     if (err == noErr) err = SendChar(ref, '[');
  247.  
  248.     // Switch on all known types of block
  249.     if (err == noErr) switch (theBlock->type) {
  250.         case bBLOK:
  251.             len = sizeof(Rec_BLOK);
  252.             ExtractBlockData(theBlock, &rBLOK, &len);
  253.             err = SendStr(ref, (char*)"\p'Adobe Premiere¬ 4.2 Generic Edit Decision List', Work_Start=%d, Work_End=%d",
  254.                 rBLOK.start, rBLOK.end);
  255.             break;
  256.         case bTRKB:
  257.             err = SendStr(ref, (char*)"\pTracks", nil);
  258.             break;
  259.         case bTRAK:
  260.             err = SendStr(ref, (char*)"\pTrack #%d", theBlock->theID);
  261.             break;
  262.         case bFVID:
  263.             err = SendStr(ref, (char*)"\pVideo", nil);
  264.             break;
  265.         case bFSUP:
  266.             err = SendStr(ref,(char*)"\pSuperImpose",nil);
  267.             break;
  268.         case bFAUD:
  269.             err = SendStr(ref, (char*)"\pAudio", nil);
  270.             break;
  271.         case bAMAP:
  272.             len = sizeof(mapping);
  273.             ExtractBlockData(theBlock, &mapping, &len);
  274.             err = SendStr(ref, (char*)"\pMapping=%b", mapping);
  275.             break;
  276.         case bFF_X:
  277.             err = SendStr(ref, (char*)"\pFX", nil);
  278.             break;
  279.         case bTREC:
  280.             len = sizeof(Rec_TREC);
  281.             ExtractBlockData(theBlock, &rTREC, &len);
  282.             err = SendStr(ref, (char*)"\pTrack_Record #%d, ClipID=%d, Start=%d, End=%d",
  283.                 theBlock->theID, rTREC.clipID, rTREC.start, rTREC.end);
  284.             break;
  285.         case bRBND:
  286.             len = sizeof(short);
  287.             ExtractBlockData(theBlock, &sValue, &len);
  288.             err = SendStr(ref, (char*)"\pRubberBand, Max=%d", sValue);
  289.             break;
  290.         case bRPNT:
  291.             len = sizeof(Rec_RPNT);
  292.             ExtractBlockData(theBlock, &rRPNT, &len);
  293.             err = SendStr(ref, (char*)"\pBand_Point #%d,h=%d,v=%d",
  294.                 theBlock->theID, rRPNT.h, rRPNT.v);
  295.             break;
  296.         case bFXOP:
  297.             len = sizeof(Rec_FXOP);
  298.             ExtractBlockData(theBlock, &rFXOP, &len);
  299.             err = SendStr(ref, (char*)"\pFX_Options,Corners=%b,Direction=%d,Start=%d,End=%d",
  300.                 rFXOP.corners, rFXOP.direction, rFXOP.startPercent, rFXOP.endPercent);
  301.             break;
  302.         case bFXDF:
  303.             len = sizeof(long);
  304.             ExtractBlockData(theBlock, &lValue, &len);
  305.             err = SendStr(ref, (char*)"\pFX_Type=%t", lValue);
  306.             break;
  307.         case bEDGE:
  308.             len = sizeof(short);
  309.             ExtractBlockData(theBlock, &sValue, &len);
  310.             err = SendStr(ref, (char*)"\pEdge,Thickness=%d", sValue);
  311.             break;
  312.         case bMPNT:
  313.             len = sizeof(Point);
  314.             ExtractBlockData(theBlock, &pt, &len);
  315.             err = SendStr(ref, (char*)"\pRef_Point,h=%d,v=%d",
  316.                 theBlock->theID, pt.h, pt.v);
  317.             break;
  318.         case bSPNT:
  319.             len = sizeof(Point);
  320.             ExtractBlockData(theBlock, &pt, &len);
  321.             err = SendStr(ref, (char*)"\pStart_Point,h=%d,v=%d",
  322.                 theBlock->theID, pt.h, pt.v);
  323.             break;
  324.         case bEPNT:
  325.             len = sizeof(Point);
  326.             ExtractBlockData(theBlock, &pt, &len);
  327.             err = SendStr(ref, (char*)"\pEnd_Point,h=%d,v=%d",
  328.                 theBlock->theID, pt.h, pt.v);
  329.             break;
  330.         case bOVER:
  331.             len = sizeof(short);
  332.             ExtractBlockData(theBlock, &sValue, &len);
  333.             GetIndString(str, 129, sValue+1);
  334.             err = SendStr(ref, (char*)"\pOverlay, Type='%s'", (long)str);
  335.             break;
  336.         case bCOLR:
  337.             len = sizeof(RGBColor);
  338.             ExtractBlockData(theBlock, &theColor, &len);
  339.             err = SendStr(ref, (char*)"\pColor,Red=%d,Green=%d,Blue=%d",
  340.                 theColor.red, theColor.green, theColor.blue);
  341.             break;
  342.         case bSIMI:
  343.             len = sizeof(short);
  344.             ExtractBlockData(theBlock, &sValue, &len);
  345.             err = SendStr(ref, (char*)"\pSimilarity=%d", sValue);
  346.             break;
  347.         case bBLND:
  348.             len = sizeof(short);
  349.             ExtractBlockData(theBlock, &sValue, &len);
  350.             err = SendStr(ref, (char*)"\pBlend=%d", sValue);
  351.             break;
  352.         case bTHRS:
  353.             len = sizeof(short);
  354.             ExtractBlockData(theBlock, &sValue, &len);
  355.             err = SendStr(ref, (char*)"\pThreshold=%d", sValue);
  356.             break;
  357.         case bCUTO:
  358.             len = sizeof(short);
  359.             ExtractBlockData(theBlock, &sValue, &len);
  360.             err = SendStr(ref, (char*)"\pCutoff=%d", sValue);
  361.             break;
  362.         case bALIA:
  363.             len = sizeof(short);
  364.             ExtractBlockData(theBlock, &sValue, &len);
  365.             err = SendStr(ref, (char*)"\pAnti-Aliasing=%d", sValue);
  366.             break;
  367.         case bSHAD:
  368.             err = SendStr(ref, (char*)"\pShadow", nil);
  369.             break;
  370.         case bRVRS:
  371.             err = SendStr(ref, (char*)"\pKey_Reversed", nil);
  372.             break;
  373.         case bGARB:
  374.             len = sizeof(Rect);
  375.             ExtractBlockData(theBlock, &box, &len);
  376.             err = SendStr(ref, (char*)"\pGarbage_Matte,Left=%d,Top=%d,Right=%d,Bottom=%d",
  377.                 box.left, box.top, box.right, box.bottom);
  378.             break;        
  379.         case bPONT:
  380.             len = sizeof(Point);
  381.             ExtractBlockData(theBlock, &pt, &len);
  382.             err = SendStr(ref, (char*)"\pPoint #%d,h=%d,v=%d", theBlock->theID, pt.h, pt.v);
  383.             break;
  384.         case bMATI:
  385.             len = sizeof(short);
  386.             ExtractBlockData(theBlock, &sValue, &len);
  387.             err = SendStr(ref, (char*)"\pMatteID=%d", sValue);
  388.             break;
  389.         case bVFLT:
  390.             err = SendStr(ref, (char*)"\pVideo_Filters", nil);
  391.             break;
  392.         case bAFLT:
  393.             err = SendStr(ref, (char*)"\pAudio_Filters", nil);
  394.             break;
  395.         case bFILT:
  396.             len = sizeof(short);
  397.             ExtractBlockData(theBlock, &sValue, &len);
  398.             err = SendStr(ref, (char*)"\pFileID=%d", sValue);
  399.             break;
  400.         case bMOTN:
  401.             len = sizeof(Rect);
  402.             ExtractBlockData(theBlock, &box, &len);
  403.             err = SendStr(ref, (char*)"\pMotion,Left=%d,Top=%d,Right=%d,Bottom=%d",
  404.                 box.left, box.top, box.right, box.bottom);
  405.             break;
  406.         case bSMTH:
  407.             err = SendStr(ref, (char*)"\pSmooth", nil);
  408.             break;
  409.         case bMREC:
  410.             len = sizeof(Rec_MREC);
  411.             ExtractBlockData(theBlock, &rMREC, &len);
  412.             err = SendStr(ref, (char*)"\pMotion_Point #%d,Zoom=%d,Time=%d,Delay=%d,Rotation=%d,h=%d,v=%d",
  413.                 theBlock->theID, rMREC.zoom, rMREC.time, rMREC.delay, rMREC.rotation,
  414.                 rMREC.spot.h, rMREC.spot.v);
  415.             break;
  416.         case bDATA:
  417.             err = SendStr(ref, (char*)"\pData #%d, %d bytes",
  418.                 theBlock->theID, theBlock->dataSize);
  419.             break;
  420.         case bCLPB:
  421.             err = SendStr(ref, (char*)"\pClips", nil);
  422.             break;
  423.         case bCLIP:
  424.             len = sizeof(Rec_CLIP);
  425.             ExtractBlockData(theBlock, &rCLIP, &len);
  426.             err = SendStr(ref, (char*)"\pClipID #%d,FileID=%d,In=%d,Out=%d", 
  427.                 theBlock->theID, rCLIP.fileID, rCLIP.in, rCLIP.out);
  428.             break;
  429.         case bMARK:
  430.             len = sizeof(long);
  431.             ExtractBlockData(theBlock, &lValue, &len);
  432.             err = SendStr(ref, (char*)"\pMark #%d, Location=%d", theBlock->theID, lValue);
  433.             break;
  434.         case bLOCK:
  435.             err = SendStr(ref, (char*)"\pAspect_Locked", nil);
  436.             break;
  437.         case bRATE:
  438.             len = sizeof(short);
  439.             ExtractBlockData(theBlock, &sValue, &len);
  440.             err = SendStr(ref, (char*)"\pRate=%d", sValue);
  441.             break;
  442.         case bFILB:
  443.             err = SendStr(ref, (char*)"\pFiles", nil);
  444.             break;
  445.         case bFILE:
  446.             err = SendStr(ref, (char*)"\pFileID #%d", theBlock->theID);
  447.             break;
  448.         case bMACS:
  449.             len = sizeof(FSSpec);
  450.             ExtractBlockData(theBlock, &rFILE, &len);
  451.             err = SendStr(ref, (char*)"\pMac_Spec,Name='%s',vRefNum=%d,parID=%d",
  452.                 (long)rFILE.name, rFILE.vRefNum, rFILE.parID);
  453.             break;
  454.         case bMACP:
  455.             len = 256;
  456.             if (p = NewPtr(256)) {
  457.                 ExtractBlockData(theBlock, p, &len);
  458.                 err = SendStr(ref, (char*)"\pMac_Path='%s'", (long)p);
  459.                 DisposePtr(p);
  460.             }
  461.             break;
  462.         case bFRMS:
  463.             len = sizeof(long);
  464.             ExtractBlockData(theBlock, &lValue, &len);
  465.             err = SendStr(ref, (char*)"\pNum_Frames=%d", lValue);
  466.             break;
  467.         case bVIDI:
  468.             len = sizeof(Rec_VIDI);
  469.             ExtractBlockData(theBlock, &rVIDI, &len);
  470.             err = SendStr(ref, (char*)"\pVideo,Width=%d,Height=%d,Depth=%d",
  471.                 rVIDI.frame.right, rVIDI.frame.bottom, rVIDI.depth);
  472.             break;
  473.         case bAUDI:
  474.             len = sizeof(long);
  475.             ExtractBlockData(theBlock, &lValue, &len);
  476.             err = SendStr(ref, (char*)"\pAudio,Rate=%d", lValue);
  477.             break;
  478.         case bTIMC:
  479.             if (pblock = NewPtr(256)) {
  480.                 len = 256;
  481.                 ExtractBlockData(theBlock, pblock, &len);
  482.                 err = SendStr(ref, (char*)"\pTimecode String='%s'", (long)pblock);
  483.                 DisposePtr(pblock);
  484.             }
  485.             break;
  486.         case bTIMB:
  487.             len = sizeof(Rec_TIMB);
  488.             ExtractBlockData(theBlock, &rTIMB, &len);
  489.             err = SendStr(ref, (char*)"\pTimecode Block,Frame=%d,DropFrame=%d,Format=%d",
  490.                 rTIMB.frames, rTIMB.dropframe, rTIMB.format);
  491.             break;
  492.         case bREEL:
  493.             if (pblock = NewPtr(256)) {
  494.                 len = 256;
  495.                 ExtractBlockData(theBlock, pblock, &len);
  496.                 err = SendStr(ref, (char*)"\pReel_Name='%s'", (long)pblock);
  497.                 DisposePtr(pblock);
  498.             }
  499.             break;
  500.         default:
  501.             err = SendStr(ref, (char*)"\p'*** Unknown: '%t' #%d, %d bytes",
  502.                 theBlock->type, theBlock->theID, theBlock->dataSize);
  503.             break;
  504.     }
  505.     
  506.     // If there are any sub-blocks call ourselves recursively to parse them.
  507.     if (err == noErr && theBlock->size > (sizeof(BlockRec) + theBlock->dataSize)) {
  508.         err = SendChar(ref, ', ');
  509.         if (!err) SendChar(ref, kReturn);
  510.         count = CountTypeBlocks(-1, theBlock);
  511.         for (i = 0; i < count && err == noErr; i++)
  512.             err = ParseBlock(FindBlock(-1, -1, i, theBlock), ref, indent + 1);
  513.         SendIndent(ref, indent);
  514.     }
  515.  
  516.     // Close this block and a comma if we're not at level 0. End with a CR.
  517.     if (err == noErr) err = SendChar(ref, ']');
  518.     if (err == noErr && indent) err = SendChar(ref, ', ');
  519.     if (err == noErr) err = SendChar(ref, kReturn);
  520.     return(err);
  521. }
  522.  
  523. //========================================================================================
  524. // Concatenate one data block onto the end of another, disposing the source handle
  525. //========================================================================================
  526. void AppendBlock(BlockRec **dstBlock, BlockRec **srcBlock)
  527. {
  528.     HandAndHand((Handle)srcBlock, (Handle)dstBlock);
  529.     DisposeHandle((Handle)srcBlock);
  530. }
  531.  
  532. //========================================================================================
  533. // Add one data block into the end of another, disposing the source handle
  534. //========================================================================================
  535. void ExtendBlock(BlockRec **dstBlock, BlockRec **srcBlock)
  536. {
  537.     HandAndHand((Handle)srcBlock, (Handle)dstBlock);
  538.     (*dstBlock)->size += GetHandleSize((Handle)srcBlock);
  539.     DisposeHandle((Handle)srcBlock);
  540. }
  541.  
  542. //========================================================================================
  543. // Skip to the next block
  544. //========================================================================================
  545. void NextBlock(BlockRec **srcBlock)
  546. {
  547.     *((Ptr*)srcBlock) += (*srcBlock)->size;
  548. }
  549.  
  550. //========================================================================================
  551. // Count the number of blocks of a particular type. Pass -1 for type to count all types.
  552. //========================================================================================
  553. long CountTypeBlocks(long type, BlockRec *srcBlock)
  554. {
  555.     long        count;
  556.     BlockRec    *end, *pos;
  557.     
  558.     count = 0;
  559.     end = (BlockRec *)(((Ptr)srcBlock) + srcBlock->size);
  560.     pos = (BlockRec *)(((Ptr)srcBlock) + sizeof(BlockRec) + srcBlock->dataSize);
  561.     while (pos < end) {
  562.         if ((type == -1) || (pos->type == type))
  563.             count++;
  564.         NextBlock(&pos);
  565.     }
  566.     return(count);
  567. }
  568.  
  569. //========================================================================================
  570. // Build a block of data with a header
  571. //========================================================================================
  572. BlockRec **BuildBlock(long type, long theID, long length, void *data)
  573. {
  574.     BlockRec    therec;
  575.     Handle        h;
  576.     
  577.     therec.size = length + sizeof(BlockRec);
  578.     therec.dataSize = length;
  579.     therec.type = type;
  580.     therec.theID = theID;
  581.     h = NewHandle(0);
  582.     PtrAndHand((Ptr)&therec, h, sizeof(BlockRec));
  583.     if (length != 0)
  584.         PtrAndHand(data, h, length);
  585.     return((BlockRec **)h);
  586. }
  587.  
  588. //========================================================================================
  589. // Find a particular block. The parameters type, theID, or index may be -1 to indicate
  590. // that they are ignored.
  591. //========================================================================================
  592. BlockRec *FindBlock(long type, long theID, long index, BlockRec *srcBlock)
  593. {
  594.     long        count;
  595.     BlockRec    *end, *pos;
  596.     
  597.     count = 0;
  598.     end = (BlockRec *)(((Ptr)srcBlock) + srcBlock->size);
  599.     pos = (BlockRec *)(((Ptr)srcBlock) + sizeof(BlockRec) + srcBlock->dataSize);
  600.     while (pos < end) {
  601.         if ((index == count) && ((type == -1) || (pos->type == type)))
  602.             return(pos);
  603.         else if ((pos->type == type && pos->theID == theID))
  604.             return(pos);
  605.         if ((type == -1) || (pos->type == type))
  606.             count++;
  607.         NextBlock(&pos);
  608.     }
  609.     return(nil);
  610. }
  611.  
  612. //========================================================================================
  613. // Return a handle to a numbered object
  614. //========================================================================================
  615. BlockRec **GetBlock(long type, long theID, long index, BlockRec **srcBlock)
  616. {
  617.     BlockRec    *destpos;
  618.     Handle        h;
  619.     
  620.     if (destpos = FindBlock(type, theID, index, *srcBlock)) {
  621.         PtrToHand((Ptr)destpos, &h, destpos->size);
  622.         return((BlockRec **)h);
  623.     } else return(nil);
  624. }
  625.  
  626. //========================================================================================
  627. // Copy the data from a numbered object
  628. //========================================================================================
  629. void ExtractBlockData(BlockRec *srcBlock, void *destination, long *maxlen)
  630. {
  631.     long    len;
  632.     
  633.     len = srcBlock->dataSize>*maxlen? *maxlen:srcBlock->dataSize;
  634.     *maxlen = len;
  635.     BlockMove(((Ptr)srcBlock)+sizeof(BlockRec), destination, len);
  636. }
  637.